package top.superflow.core.semantic

import java.lang.reflect.Method

import org.apache.commons.lang3.builder.ToStringBuilder
import top.superflow.core.annotation.{SemanticDropped, SemanticType}
import top.superflow.lang.{ArrayUtils, StringUtils}
import top.superflow.utils.ClassUtils

import scala.collection.{Map, Set}
import scala.collection.mutable.HashMap

class SemanticUtils 

object SemanticUtils{
  
  def getSemantic(method : Method) : SemanticData = {
    if(method == null){
      return new SemanticData
    }
    
    toBean(method.getDeclaredAnnotation(classOf[SemanticDropped]))
  }
  
  def getSupportedSemantic(targetClass : Class[_]) : Map[SemanticData, Method] = {
    val supportedSemantics = HashMap[SemanticData, Method]()
    val methodsWithSemantic = ClassUtils.getMethod[SemanticDropped](targetClass);
    
    if(methodsWithSemantic == null || methodsWithSemantic.isEmpty){
      return supportedSemantics
    }
    
    for((semantic,method) <- methodsWithSemantic) yield {
      
      supportedSemantics += (toBean(semantic) -> method)
    }
    
    supportedSemantics
  }
  
  def createKeyWordSemantic(semanticWords : String) : SemanticData = {
    val semantic = createSemanticByMode(semanticWords, SemanticType.KEYWORD)
    
    semantic.setKeyWords(extractKeyWords(semanticWords))
    
    semantic
  }
  
  def createSynonymSemantic(semanticWords : String) : SemanticData = {
    val semantic = createSemanticByMode(semanticWords, SemanticType.SYNONYM)
    
    semantic.setSynonym(Set[String](cleanSynonym(semanticWords)))
    
    semantic
  }
  
  private def extractKeyWords(semanticWords : String) : Set[String] = {
    val words = StringUtils.split(semanticWords, StringUtils.SINGLE_SPACES)
    val cleanWords = Set[String]()
    for(word <- words){
      val trimmedWord = StringUtils.trim(word)
      if(!StringUtils.isBlank(trimmedWord)){
        cleanWords +: StringUtils.lowerCase(trimmedWord)
      }
    }
    
    cleanWords
  }
  
  
  
  private def createSemanticByMode(semanticWords : String, semanticType : SemanticType) : SemanticData = {
    val semantic = new SemanticData
    semantic.setMode(semanticType)
    semantic
  }
  
  def getSupportedSemantic(targetClass : Class[_], returnType : Class[_]) : Map[SemanticData, Method] = {
    val methodsSuportedSpecificClass = getSupportedSemantic(targetClass)
    val methodsSupportedReturnType = HashMap[SemanticData, Method]()
    
    for((semantic, method) <- methodsSuportedSpecificClass){
      if(method.getReturnType == returnType){
        methodsSupportedReturnType += (semantic -> method)
      }
    }
    
    methodsSupportedReturnType
  }
  
  private def toBean(semantic : SemanticDropped) : SemanticData = {
    if(semantic == null){
      return null
    }
    
    val bean = new SemanticData
    bean.setDescription(semantic.description())
    bean.setKeyWords(cleanWords(semantic.keyWords()))
    bean.setSynonym(cleanWords(semantic.synonyms()))
    bean.setMode(semantic.mode())
    
    bean
  }
  
  
  private def cleanWords(words : Array[String]) : Set[String] = {
    val cleaned = Set[String]()
    if(ArrayUtils.isEmpty(words)){
      return cleaned
    }
    
    for(word <- words){
      cleaned +: cleanSynonym(word)
    }
    
    cleaned
  }
  
  private def cleanSynonym(synonym : String) : String = {
    if(StringUtils.isBlank(synonym)){
      return StringUtils.EMPTY
    }
    
    var cleaned = cleanText(synonym)
    
    while(StringUtils.contains(cleaned, StringUtils.DOUBLE_SPACES)){
      StringUtils.replaceOnce(cleaned, StringUtils.DOUBLE_SPACES, StringUtils.SINGLE_SPACES)
    }
    
    cleaned
  }
  
  private def cleanText(words : String) : String = {
    StringUtils.lowerCase(StringUtils.trim(words))
  }
  
  def isSemanticEqual(str : String, semantic : SemanticData) : Boolean = {
    if(StringUtils.isBlank(str) || semantic == null){
      return false
    }
    
    isSemanticKeyEqual(str, semantic) || isSemanticSynonymsEqual(str, semantic)
  }
  
  private def isSemanticKeyEqual (str : String, semantic : SemanticData) : Boolean = {
    if(semantic.getKeyWords().isEmpty){
      return false
    }
    
    for(key <- semantic.getKeyWords()){
      if(!StringUtils.containsIgnoreCase(str, StringUtils.trim(key))){
        return false
      }
    }
    
    true
  }
  
  def equals(semantic1 : SemanticData, semantic2 : SemanticData) : Boolean = {
    if(semantic1 == null 
        || semantic2 == null
        || semantic1.getMode() != semantic2.getMode()){
      return false
    }
    
    semantic1.getMode() match {
      case SemanticType.KEYWORD => return isKeyWordsEqual(semantic1.getKeyWords(), semantic2.getKeyWords())
      case SemanticType.SYNONYM => return isSynonymsMatch(semantic1.getSynonyms(), semantic2.getSynonyms())
      case _ => false
    }
  }
  
  private def isSynonymsMatch (synonyms1 : Set[String], synonyms2 : Set[String]) : Boolean = {
    if(synonyms1 == null || synonyms1.isEmpty ||
        synonyms2 == null || synonyms2.isEmpty){
      return false
    }
    
    for(syn1 <- synonyms1){
      if(synonyms2.contains(syn1)){
        return true
      }
    }
    
    false
  }
  
  private def isKeyWordsEqual(keyWords1 : Set[String], keyWords2 : Set[String]) : Boolean = {
    if(keyWords1 == null || keyWords1.isEmpty ||
        keyWords2 == null || keyWords2.isEmpty ||
        keyWords1.size != keyWords2.size){
      return false
    }
    
    for(key1 <- keyWords1){
      if(!keyWords2.contains(key1)){
        return false
      }
    }
    
    true
  }
  
  private def isSemanticSynonymsEqual (str : String, semantic : SemanticData) : Boolean = ???

  def main(args: Array[String]): Unit = {
    val test = new SemanticTest

    for(method <- test.getClass.getDeclaredMethods){
      println(ToStringBuilder.reflectionToString(method))
    }
  }
}

